package com.liuhao.ssm.annotation;

import java.lang.reflect.Method;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.liuhao.ssm.domain.SysLog;
import com.liuhao.ssm.domain.UserInfo;
import com.liuhao.ssm.service.intf.SysLogService;
import com.liuhao.ssm.util.ResultData;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * Created by liuHao on 2017/5/25.
 */

@Aspect
@Component
public class SystemLogAspect implements Ordered {

    //注入Service用于把日志保存数据库
    @Autowired
    private SysLogService sysLogService;

    private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    //Controller层切点
    @Pointcut("execution (* com.liuhao.ssm.controller..*.*(..))")
    public void controllerAspect() {
    }

    //Service层切点
    @Pointcut("execution (* com.liuhao.ssm.service.impl.*.*(..))")
    public void serviceAspect() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的退出操作
     *
     * @param joinPoint 切点
     */
    @Before("execution (* com.liuhao.ssm.controller.LoginController.logout())")
    public void doBefore(JoinPoint joinPoint) throws ClassNotFoundException {
        System.out.println("==========执行controller前置通知===============");
        if (logger.isInfoEnabled()) {
            logger.info("before " + joinPoint);
        }
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //读取session中的用户
        UserInfo user = (UserInfo) session.getAttribute("LoginInfo");
        String userName;
        if (user != null) {
            userName = user.getUserName();
        } else {
            userName = joinPoint.getArgs()[0].toString();
        }
        //请求的IP
        String ip = request.getRemoteAddr();

        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String operationType = getOperationType(methods, methodName, arguments);
        String operationName = getOperationName(methods, methodName, arguments);
        //*========控制台输出=========*//
        System.out.println("=====controller后置通知开始=====");
        System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
        System.out.println("请求结果:" + "退出成功");
        System.out.println("方法描述:" + operationName);
        System.out.println("请求人:" + userName);
        System.out.println("请求IP:" + ip);
        //*========数据库日志=========*//
        SysLog log = new SysLog();
        log.setDescription(operationName);
        log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
        log.setResult("退出成功");
        log.setLogType(operationType);
        log.setRequestIp(ip);
        log.setExceptionCode(null);
        log.setExceptionDetail(null);
        log.setParams(null);
        log.setCreateBy(userName);
        log.setCreateDate(new Date());
        //保存数据库
        sysLogService.insert(log);
    }
//
//    //配置controller环绕通知,使用在方法aspect()上注册的切入点
//    @Around("controllerAspect()")
//    public ResultData around(ProceedingJoinPoint joinPoint) throws Throwable {
//        System.out.println("==========开始执行controller环绕通知===============");
//        long start = System.currentTimeMillis();
//        ResultData res = (ResultData) joinPoint.proceed();
//        try {
//            ((ProceedingJoinPoint) joinPoint).proceed();
//            long end = System.currentTimeMillis();
//            if (logger.isInfoEnabled()) {
//                logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
//            }
//            System.out.println("==========结束执行controller环绕通知===============");
//        } catch (Throwable e) {
//            long end = System.currentTimeMillis();
//            if (logger.isInfoEnabled()) {
//                logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
//            }
//        }
//        return res;
//    }
//
//    /**
//     * 后置通知 用于拦截Controller层记录用户的操作
//     *
//     * @param joinPoint 切点
//     */
//    @After("controllerAspect()")
//    public void after(JoinPoint joinPoint) {
//
//        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        HttpSession session = request.getSession();
//        //读取session中的用户
//        UserInfo user = (UserInfo) session.getAttribute("LoginInfo");
//        //请求的IP
//        String ip = request.getRemoteAddr();
//        try {
//
//            String targetName = joinPoint.getTarget().getClass().getName();
//            String methodName = joinPoint.getSignature().getName();
//            Object[] arguments = joinPoint.getArgs();
//            Class targetClass = Class.forName(targetName);
//            Method[] methods = targetClass.getMethods();
//            String operationType = "";
//            String operationName = "";
//            for (Method method : methods) {
//                if (method.getName().equals(methodName)) {
//                    Class[] clazzs = method.getParameterTypes();
//                    if (clazzs.length == arguments.length) {
//                        operationType = method.getAnnotation(Log.class).operationType();
//                        operationName = method.getAnnotation(Log.class).operationName();
//                        break;
//                    }
//                }
//            }
//            //*========控制台输出=========*//
//            System.out.println("=====controller后置通知开始=====");
//            System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
//            System.out.println("方法描述:" + operationName);
//            System.out.println("请求人:" + user.getUserName());
//            System.out.println("请求IP:" + ip);
//            //*========数据库日志=========*//
//            SysLog log = new SysLog();
//            log.setDescription(operationName);
//            log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
//            log.setLogType("操作日志");
//            log.setRequestIp(ip);
//            log.setExceptionCode(null);
//            log.setExceptionDetail(null);
//            log.setParams(null);
//            log.setCreateBy(user.getUserName());
//            log.setCreateDate(new Date());
//            //保存数据库
//            sysLogService.insert(log);
//            System.out.println("=====controller后置通知结束=====");
//        } catch (Exception e) {
//            //记录本地异常日志
//            logger.error("==后置通知异常==");
//            logger.error("异常信息:{}", e.getMessage());
//        }
//    }

    //配置后置返回通知,使用在方法aspect()上注册的切入点
    @AfterReturning(value = "controllerAspect()", returning = "res")
    public void afterReturn(JoinPoint joinPoint, ResultData res) throws ClassNotFoundException {
        System.out.println("=====执行controller后置返回通知=====");
        if (logger.isInfoEnabled()) {
            logger.info("afterReturn " + joinPoint);
        }
        /************************************************************************/
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //读取session中的用户
        UserInfo user = (UserInfo) session.getAttribute("LoginInfo");
        String userName;
        if (user != null) {
            userName = user.getUserName();
        } else {
            userName = joinPoint.getArgs()[0].toString();
        }
        //请求的IP
        String ip = request.getRemoteAddr();
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String operationType = getOperationType(methods, methodName, arguments);
            String operationName = getOperationName(methods, methodName, arguments);
            //*========控制台输出=========*//
            System.out.println("=====controller后置通知开始=====");
            System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
            System.out.println("请求结果:" + res.get("msg") == null ? "失败" : res.get("msg").toString());
            System.out.println("方法描述:" + operationName);
            System.out.println("请求人:" + userName);
            System.out.println("请求IP:" + ip);
            //*========数据库日志=========*//
            SysLog log = new SysLog();
            log.setDescription(operationName);
            log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
            log.setResult(res.get("msg") == null ? "失败" : res.get("msg").toString());
            log.setLogType(operationType);
            log.setRequestIp(ip);
            log.setExceptionCode(null);
            log.setExceptionDetail(null);
            log.setParams(null);
            log.setCreateBy(userName);
            log.setCreateDate(new Date());
            //保存数据库
            sysLogService.insert(log);
        } catch (Exception ex) {
            //记录本地异常日志
            logger.error("==异常通知异常==");
            logger.error("异常信息:{}", ex.getMessage());
        }
         /*==========记录本地异常日志==========*/
        //logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);

        /************************************************************************/
    }

    /**
     * 异常通知 用于拦截记录异常日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //读取session中的用户
        UserInfo user = (UserInfo) session.getAttribute("LoginInfo");
        //获取请求ip
        String ip = request.getRemoteAddr();
        //获取用户请求方法的参数并序列化为JSON格式字符串
        String params = "";
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                params += (joinPoint.getArgs()[i]).toString() + ";";
            }
        }
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String operationType = getOperationType(methods, methodName, arguments);
            String operationName = getOperationName(methods, methodName, arguments);
             /*========控制台输出=========*/
            System.out.println("=====异常通知开始=====");
            System.out.println("异常代码:" + e.getClass().getName());
            System.out.println("异常信息:" + e.getMessage());
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
            System.out.println("请求结果:" + "异常");
            System.out.println("方法描述:" + operationName);
            System.out.println("请求人:" + user.getUserName());
            System.out.println("请求IP:" + ip);
            System.out.println("请求参数:" + params);
               /*==========数据库日志=========*/
            SysLog log = new SysLog();
//            log.setId(UUID.randomUUID().toString());
            log.setDescription(operationName);
            log.setExceptionCode(e.getClass().getName());
            log.setLogType("异常日志");
            log.setExceptionDetail(e.getMessage());
            log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
            log.setResult("异常");
            log.setParams(params);
            log.setCreateBy(user.getUserName());
            log.setCreateDate(new Date());
            log.setRequestIp(ip);
            //保存数据库
            sysLogService.insert(log);
            System.out.println("=====异常通知结束=====");
        } catch (Exception ex) {
            //记录本地异常日志
            logger.error("==异常通知异常==");
            logger.error("异常信息:{}", ex.getMessage());
        }
         /*==========记录本地异常日志==========*/
        logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);

    }

    @Override
    public int getOrder() {
        return 1;
    }

    private String getOperationType(Method[] methods, String methodName, Object[] arguments) {
        String type = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    type = method.getAnnotation(Log.class).operationType();
                    break;
                }
            }
        }
        return type;
    }

    private String getOperationName(Method[] methods, String methodName, Object[] arguments) {
        String name = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    name = method.getAnnotation(Log.class).operationName();
                    break;
                }
            }
        }
        return name;
    }

}
